<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id='root'></div>
<script>

// 收集所有的响应式对象：Map(obj => proxy)
const reactiveMap = new WeakMap()

const handler = {
    get(target, key) {
        track(target, key);
        if(typeof target[key] === 'object') return reactive(target[key]); // 懒响应式处理： get时如果目标为对象，则进行响应式处理
        return target[key];
    },
    set(target, key, value) {
        target[key] = value;
        trigger(target, key);
    }
}

// 核心就是返回obj的Proxy代理对象，但是为了防止对一个obj重复创建Proxy对象，我们在reactiveMap中记录创建历史
function reactive(obj) {
    const existingProxy = reactiveMap.get(obj);
    if(existingProxy) return existingProxy;
    let proxy = new Proxy(obj, handler);
    reactiveMap.set(obj, proxy);
    return proxy;
}

const obj = {
    name: 'jrd',
    age: 21,
    job: {
        address: 'beiJing'
    }
}

let activeEffect;

function effect(fn) {
    const _effect = new ReactiveEffect(fn);
    _effect.run();
}

class ReactiveEffect {
    constructor(fn, ) {
        this.fn = fn;
    }
    run() {
        activeEffect = this;
        return this.fn();
    }
}

let targetMap = new WeakMap(); // 响应式对象 => (属性 => 回调Set)


// track与trigger说白了就是明确了——响应式对象 => (属性 => 回调Set) ，即Map(obj => Map(attr => Set(callback)))这种依赖存储结构之后，逐层的去访问与构建
function track(target, key) {
    let attrMap = targetMap.get(target);
    if(!attrMap) {
        targetMap.set(target, (attrMap = new Map()))
    }

    let callbackSet = attrMap.get(key);
    if(!callbackSet) {
        attrMap.set(key, (callbackSet = new Set()))
    }

    callbackSet.add(activeEffect);
}

function trigger(target, key) {
    let attrMap = targetMap.get(target);
    if(!attrMap) {
        return;
    }

    let callbackSet = attrMap.get(key);
    if(!callbackSet) {
        return;
    }

    for(let effect of callbackSet) {
        effect.run();
    }
}

const reactiveObj = reactive(obj);

let rootEl = document.getElementById('root');
effect(() => {
    rootEl.innerText = reactiveObj.job.address;
})

setTimeout(() => {
    reactiveObj.job.address += 'xxx';

    console.log(targetMap);
    console.log(reactiveObj);
}, 1000)



</script>
</body>
</html>